package org.chen.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.chen.dto.RateLimiterConfigDTO;
import org.chen.mapper.RateLimiterConfigMapper;
import org.chen.model.RateLimiterConfig;
import org.chen.service.IRateLimiterConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class RateLimiterConfigServiceImpl extends ServiceImpl<RateLimiterConfigMapper, RateLimiterConfig> implements IRateLimiterConfigService {
    private static final String REDIS_CONFIG_KEY = "rate_limiter:config";
    private static final int REDIS_EXPIRE_TIME = 24; // 24小时过期

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    public RateLimiterConfigDTO getConfig() {
        // 先从Redis获取
        String configJson = redisTemplate.opsForValue().get(REDIS_CONFIG_KEY);
        if (configJson != null) {
            try {
                return objectMapper.readValue(configJson, RateLimiterConfigDTO.class);
            } catch (Exception e) {
                log.error("解析Redis配置失败", e);
            }
        }

        // Redis没有，从数据库获取
        return getConfigFromDB();
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateConfig(RateLimiterConfigDTO configDTO) {
        LocalDateTime now = LocalDateTime.now();

        // 更新数据库
        if (configDTO.getMaxConcurrentPerDomain() != null) {
            updateConfig("max_concurrent_per_domain", configDTO.getMaxConcurrentPerDomain(), now);
        }
        if (configDTO.getGlobalConcurrent() != null) {
            updateConfig("global_concurrent", configDTO.getGlobalConcurrent(), now);
        }
        if (configDTO.getMinInterval() != null) {
            updateConfig("min_interval", configDTO.getMinInterval(), now);
        }
        if (configDTO.getMaxInterval() != null) {
            updateConfig("max_interval", configDTO.getMaxInterval(), now);
        }
        if (configDTO.getDomainInterval() != null) {
            updateConfig("domain_interval", configDTO.getDomainInterval(), now);
        }

        // 更新Redis
        saveToRedis(configDTO);
    }

    private void updateConfig(String key, Integer value, LocalDateTime updateTime) {
        this.lambdaUpdate()
                .eq(RateLimiterConfig::getConfigKey, key)
                .set(RateLimiterConfig::getConfigValue, value)
                .set(RateLimiterConfig::getUpdateTime, updateTime)
                .update();
    }

    private RateLimiterConfigDTO getConfigFromDB() {
        List<RateLimiterConfig> configs = this.list();
        RateLimiterConfigDTO configDTO = new RateLimiterConfigDTO();

        for (RateLimiterConfig config : configs) {
            switch (config.getConfigKey()) {
                case "max_concurrent_per_domain" -> configDTO.setMaxConcurrentPerDomain(config.getConfigValue());
                case "global_concurrent" -> configDTO.setGlobalConcurrent(config.getConfigValue());
                case "min_interval" -> configDTO.setMinInterval(config.getConfigValue());
                case "max_interval" -> configDTO.setMaxInterval(config.getConfigValue());
                case "domain_interval" -> configDTO.setDomainInterval(config.getConfigValue());
            }
        }

        return configDTO;
    }

    public void saveToRedis(RateLimiterConfigDTO configDTO) {
        try {
            String configJson = objectMapper.writeValueAsString(configDTO);
            redisTemplate.opsForValue().set(
                    REDIS_CONFIG_KEY,
                    configJson,
                    REDIS_EXPIRE_TIME,
                    TimeUnit.HOURS
            );
        } catch (Exception e) {
            log.error("保存配置到Redis失败", e);
        }
    }
}
